﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Fast.Framework.Models;


namespace Fast.Framework.Extensions
{

    /// <summary>
    /// 表达式扩展类
    /// </summary>
    public static class ExpressionExtensions
    {
        /// <summary>
        /// 解析Sql
        /// </summary>
        /// <param name="expression">表达式</param>
        /// <param name="options">选项</param>
        /// <returns></returns>
        public static SqlInfo ResolveSql(this Expression expression, SqlExpressionOptions options)
        {
            var ex = new SqlExpressionVisitor(options);
            ex.Visit(expression);
            return ex._sqlInfo;
        }
    }

    /// <summary>
    /// 解析类型
    /// </summary>
    [Flags]
    public enum ResolveType
    {
        /// <summary>
        /// Sql字符串
        /// </summary>
        SqlString = 0,

        /// <summary>
        /// 调用
        /// </summary>
        Call = 1,

        /// <summary>
        /// 不
        /// </summary>
        Not = 2,

        /// <summary>
        /// 左相似
        /// </summary>
        LeftLike = 3,

        /// <summary>
        /// 右相似
        /// </summary>
        RightLike = 4,

        /// <summary>
        /// 相似
        /// </summary>
        Like = 5
    }

    /// <summary>
    /// 表达式映射
    /// </summary>
    public static class ExpressionMapper
    {
        /// <summary>
        /// 表达式类型
        /// </summary>
        public static readonly Dictionary<ExpressionType, string> _expressionTypes;

        /// <summary>
        /// 方法调用
        /// </summary>
        public static readonly Dictionary<string, Func<ResolveType, DbType, SqlInfo, MethodCallExpression, Func<Expression, Expression>, Expression>> _methodCall;

        /// <summary>
        /// 构造方法
        /// </summary>
        static ExpressionMapper()
        {
            _expressionTypes = new Dictionary<ExpressionType, string>()
            {
                {ExpressionType.Add,"+" },
                {ExpressionType.Subtract,"-" },
                {ExpressionType.Multiply,"*" },
                {ExpressionType.Divide,"/" },
                {ExpressionType.AndAlso,"AND" },
                {ExpressionType.OrElse,"OR" },
                {ExpressionType.Equal,"=" },
                {ExpressionType.NotEqual,"<>" },
                {ExpressionType.GreaterThan,">" },
                {ExpressionType.LessThan,"<" },
                {ExpressionType.GreaterThanOrEqual,">=" },
                {ExpressionType.LessThanOrEqual,"<=" }
            };

            _methodCall = new Dictionary<string, Func<ResolveType, DbType, SqlInfo, MethodCallExpression, Func<Expression, Expression>, Expression>>()
            {
                #region 聚合函数
                {"Sum", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("SUM");
                    return node;
                } },
                {"Max", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("MAX");
                    return node;
                } },
                {"Min", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("MIN");
                    return node;
                } },
                {"Count", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("COUNT");
                    return node;
                } },
                {"Avg", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("AVG");
                    return node;
                } },
                #endregion

                #region 日期函数
                {"AddYears", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(YEAR,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" YEAR)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'YEAR')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" YEAR'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" YEAR')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddMonths", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(MONTH,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" MONTH)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'MONTH')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" MONTH'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" MONTH')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddDays", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(DAY,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" DAY)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'DAY')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" DAY'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" DAY')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddHours", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(HOUR,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" HOUR)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'HOUR')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" HOUR'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" HOUR')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddMinutes", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(MINUTE,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" MINUTE)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'MINUTE')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" MIN'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" MINUTE')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddSeconds", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(SECOND,");
                    }
                    else if(dbType==DbType.MySQL)
                    {
                        sql.SqlStack.Push(" SECOND)");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",INTERVAL ");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATE_ADD(");
                    }
                    else if(dbType==DbType.Oracle)
                    {
                        sql.SqlStack.Push(",'SECOND')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + NUMTODSINTERVAL(");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.PostgreSQL)
                    {
                        sql.SqlStack.Push(" SEC'");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(" + '");
                        visit.Invoke(node.Object);
                    }
                    else if(dbType==DbType.SQLite)
                    {
                        sql.SqlStack.Push(" SECOND')");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push(",'");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push("DATETIME(");
                    }
                    return node;
                } },
                {"AddMilliseconds", (resolveType,dbType,sql,node,visit)=>
                {
                    if(dbType==DbType.SQLServer)
                    {
                        sql.SqlStack.Push(")");
                        visit.Invoke(node.Object);
                        sql.SqlStack.Push(",");
                        visit.Invoke(node.Arguments[0]);
                        sql.SqlStack.Push("DATEADD(MILLISECOND,");
                    }
                    else
                    {
                        throw new Exception($"AddMilliseconds 不被支持,因为{dbType}不支持添加毫秒");
                    }
                    return node;
                } },
                {"GetDate", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("GETDATE");
                    return node;
                } },
                {"Year", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("YEAR");
                    return node;
                } },
                {"Month", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("MONTH");
                    return node;
                } },
                {"Day", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("DAY");
                    return node;
                } },
                {"Current_Timestamp", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("CURRENT_TIMESTAMP");
                    return node;
                } },
                {"Current_Date", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("CURRENT_DATE");
                    return node;
                } },
                {"Current_Time", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("CURRENT_TIME");
                    return node;
                } },
                #endregion

                #region 数学函数
                {"Abs", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("ABS");
                    return node;
                } },
                {"Round", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[1]);
                    sql.SqlStack.Push(",");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("ROUND");
                    return node;
                } },
                #endregion

                #region 字符串函数
                {"Contains", (resolveType,dbType,sql,node,visit)=>
                {
                    var isNot=resolveType.HasFlag(ResolveType.Not);
                    resolveType=ResolveType.Like;
                    visit.Invoke(node.Arguments[0]);
                    resolveType=ResolveType.SqlString;
                    sql.SqlStack.Push($"{(isNot?" NOT":"")} LIKE ");
                    visit.Invoke(node.Object);
                    return node;
                } },
                {"StartsWith", (resolveType,dbType,sql,node,visit)=>
                {
                    var isNot=resolveType.HasFlag(ResolveType.Not);
                    resolveType=ResolveType.LeftLike;
                    visit.Invoke(node.Arguments[0]);
                    resolveType=ResolveType.SqlString;
                    sql.SqlStack.Push($"{(isNot?" NOT":"")} LIKE ");
                    visit.Invoke(node.Object);
                    return node;
                } },
                {"EndsWith", (resolveType,dbType,sql,node,visit)=>
                {
                    var isNot=resolveType.HasFlag(ResolveType.Not);
                    resolveType=ResolveType.LeftLike;
                    visit.Invoke(node.Arguments[0]);
                    resolveType=ResolveType.SqlString;
                    sql.SqlStack.Push($"{(isNot?" NOT":"")} LIKE ");
                    visit.Invoke(node.Object);
                    return node;
                } },
                {"Substring", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    if (node.Arguments.Count > 1)
                    {
                        visit.Invoke(node.Arguments[1]);
                        sql.SqlStack.Push(",");
                    }
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push(",");
                    visit.Invoke(node.Object);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("SUBSTRING");
                    return node ;
                } },
                {"Replace", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[1]);
                    sql.SqlStack.Push(",");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push(",");
                    visit.Invoke(node.Object);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("REPLACE");
                    return node;
                } },
                {"ToUpper", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Object);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("UPPER");
                    return node;
                } },
                {"ToLower", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Object);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("LOWER");
                    return node ;
                } },
                {"Trim", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Object);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("TRIM");
                    return node;
                } },
                #endregion

                #region 其它函数
                {"Equals", (resolveType,dbType,sql,node,visit)=>
                {
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push($" {(resolveType.HasFlag(ResolveType.Not)?"<>":"=")} ");
                    visit.Invoke(node.Object);
                    return node;
                } },
                {"IsNull", (resolveType,dbType,sql,node,visit)=>
                {
                    sql.SqlStack.Push(")");
                    visit.Invoke(node.Arguments[1]);
                    sql.SqlStack.Push(",");
                    visit.Invoke(node.Arguments[0]);
                    sql.SqlStack.Push("(");
                    sql.SqlStack.Push("ISNULL");
                    visit.Invoke(node.Object);
                    return node;
                } },
	            #endregion
            };
        }
    }

    /// <summary>
    /// Sql表达式访问
    /// </summary>
    public class SqlExpressionVisitor : ExpressionVisitor
    {

        /// <summary>
        /// Sql信息
        /// </summary>
        public readonly SqlInfo _sqlInfo;

        /// <summary>
        /// 参数索引
        /// </summary>
        private readonly Dictionary<string, int> _parameterIndex;

        /// <summary>
        /// 成员信息
        /// </summary>
        private readonly Stack<MemberInfo> _memberInfos;

        /// <summary>
        /// 表达式选项
        /// </summary>
        private readonly SqlExpressionOptions _options;

        /// <summary>
        /// 解析类型
        /// </summary>
        private ResolveType _resolveType;

        /// <summary>
        /// 子调用
        /// </summary>
        private bool _subcall;

        /// <summary>
        /// 相等
        /// </summary>
        private bool _equal;

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="options">表达式选项</param>
        public SqlExpressionVisitor(SqlExpressionOptions options, ResolveType resolveType = ResolveType.SqlString, bool subcall = false)
        {
            _sqlInfo = new SqlInfo();
            _parameterIndex = new Dictionary<string, int>();
            _memberInfos = new Stack<MemberInfo>();
            this._options = options;
            this._resolveType = resolveType;
            this._subcall = subcall;
        }

        /// <summary>
        /// 访问
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        [return: NotNullIfNotNull("node")]
        public override Expression Visit(Expression node)
        {
            //Console.WriteLine(node?.NodeType);
            return base.Visit(node);
        }

        /// <summary>
        /// 访问Lambda
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            if (!_options.IgnoreParameterExpression)
            {
                var index = 0;
                foreach (var parameter in node.Parameters)
                {
                    _parameterIndex.Add(parameter.Name, index);
                    index++;
                }
            }
            return Visit(node.Body);
        }

        /// <summary>
        /// 访问一元表达式
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.ArrayLength)
            {
                var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
                sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
                var array = (sqlExpressionVisitor.Visit(node.Operand) as ConstantExpression).Value as object[];
                return Visit(Expression.Constant(array.Length));
            }
            if (node.NodeType == ExpressionType.Not)
            {
                var oldResolveType = _resolveType;//原类型
                _resolveType = ResolveType.Not | oldResolveType;//拼接后的类型
            }
            return Visit(node.Operand);
        }

        /// <summary>
        /// 访问二元表达式
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node.NodeType == ExpressionType.ArrayIndex)
            {
                var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
                sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
                var array = (sqlExpressionVisitor.Visit(node.Left) as ConstantExpression).Value as object[];
                var value = array[Convert.ToInt32((sqlExpressionVisitor.Visit(node.Right) as ConstantExpression).Value)];
                return VisitConstant(Expression.Constant(value));
            }

            if (!ExpressionMapper._expressionTypes.ContainsKey(node.NodeType))
            {
                throw new Exception($"暂不支持{node.NodeType}类型解析");
            }

            Expression rightExpression = Expression.Constant(null);

            var op = ExpressionMapper._expressionTypes[node.NodeType];

            bool isBinaryExpression;

            isBinaryExpression = node.Right is BinaryExpression && node.Right.NodeType != ExpressionType.ArrayIndex;

            if (isBinaryExpression)
            {
                _sqlInfo.SqlStack.Push(")");
            }

            _equal = _resolveType.HasFlag(ResolveType.SqlString) && node.Right.Type.Equals(typeof(bool)) && node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual;

            if (node.Right.NodeType == ExpressionType.Constant && (node.Right as ConstantExpression).Value == null)
            {
                if (op == "=")
                {
                    _sqlInfo.SqlStack.Push(" IS NULL");
                }
                else if (op == "<>")
                {
                    _sqlInfo.SqlStack.Push(" IS NOT NULL");
                }
            }
            else
            {
                rightExpression = Visit(node.Right);
            }

            if (isBinaryExpression)
            {
                _sqlInfo.SqlStack.Push("(");
            }

            _sqlInfo.SqlStack.Push($" {op} ");

            isBinaryExpression = node.Left is BinaryExpression && node.Left.NodeType != ExpressionType.ArrayIndex;

            if (isBinaryExpression)
            {
                _sqlInfo.SqlStack.Push(")");
            }

            _equal = _resolveType.HasFlag(ResolveType.SqlString) && node.Left.Type.Equals(typeof(bool)) && node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual;

            Visit(node.Left);

            if (isBinaryExpression)
            {
                _sqlInfo.SqlStack.Push("(");
            }
            _equal = false;
            return rightExpression;
        }

        /// <summary>
        /// 访问索引
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitIndex(IndexExpression node)
        {
            return base.VisitIndex(node);
        }

        /// <summary>
        /// 访问参数
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (!_options.IgnoreParameterExpression)
            {
                _sqlInfo.SqlStack.Push($"p{_parameterIndex[node.Name]}.");
            }
            return node;
        }

        /// <summary>
        /// 访问有条件
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitConditional(ConditionalExpression node)
        {
            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
            var constantExpression = sqlExpressionVisitor.Visit(node.Test) as ConstantExpression;
            if (Convert.ToBoolean(constantExpression.Value))
            {
                return Visit(node.IfTrue);
            }
            return Visit(node.IfFalse);
        }

        /// <summary>
        /// 访问对象
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitNew(NewExpression node)
        {
            if (_resolveType.HasFlag(ResolveType.SqlString))
            {
                for (int i = 0; i < node.Arguments.Count; i++)
                {
                    var name = AddIdentifier(node.Members[i].IsDefined(typeof(ColumnAttribute)) ? node.Members[i].GetCustomAttribute<ColumnAttribute>().Name : node.Members[i].Name);
                    Visit(node.Arguments[i]);
                    var value = string.Join("", _sqlInfo.SqlStack.ToList());
                    _sqlInfo.NewKeyValues.Add(name, value);
                    _sqlInfo.NewNames.Add(name);
                    _sqlInfo.NewValues.Add(value);
                    _sqlInfo.NewAssignMapper.Add($"{name} = {(value == "" ? "NULL" : value)}");
                    _sqlInfo.NewAsMapper.Add($"{(value == "" ? "NULL" : value)} AS {name}");
                    _sqlInfo.SqlStack.Clear();
                }
                return node;
            }
            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
            var args = new List<object>();
            foreach (var item in node.Arguments)
            {
                args.Add((sqlExpressionVisitor.Visit(item) as ConstantExpression).Value);
            }

            var type = node.Type;

            object obj = null;

            if (type.IsGenericTypeDefinition)//泛型类
            {
                type = type.MakeGenericType(type.GetGenericArguments());
            }
            if (type.IsDefined(typeof(CompilerGeneratedAttribute)))//匿名对象
            {
                var argsTypes = args.Select(s => s?.GetType()).ToList().GetTypeDefaultValue();
                obj = Activator.CreateInstance(type, argsTypes.ToArray());
                for (int i = 0; i < node.Members.Count; i++)
                {
                    var member = node.Members[i];
                    type.GetField($"<{member.Name}>i__Field", BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance).SetValue(obj, args[i]);
                }
            }
            else
            {
                obj = Activator.CreateInstance(type, args.ToArray());
            }
            return VisitConstant(Expression.Constant(obj));
        }

        /// <summary>
        /// 访问列表初始化
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitListInit(ListInitExpression node)
        {
            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
            var args = new List<object>();
            args.Add((sqlExpressionVisitor.Visit(node.NewExpression) as ConstantExpression).Value);

            var type = node.Type;
            if (type.IsGenericTypeDefinition)
            {
                type = type.MakeGenericType(type.GetGenericArguments());
            }
            var list = Activator.CreateInstance(type, args.ToArray());
            foreach (var item in node.Initializers)
            {
                var args2 = new List<object>();
                foreach (var item2 in item.Arguments)
                {
                    args2.Add((sqlExpressionVisitor.Visit(item2) as ConstantExpression).Value);
                }
                item.AddMethod.Invoke(list, args2.ToArray());
            }
            if (_resolveType.HasFlag(ResolveType.SqlString))
            {
                return VisitConstant(Expression.Constant(string.Join(",", list)));
            }

            return VisitConstant(Expression.Constant(list));
        }

        /// <summary>
        /// 访问数组
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
            int length;
            if (node.NodeType == ExpressionType.NewArrayBounds)
            {
                length = Convert.ToInt32((sqlExpressionVisitor.Visit(node.Expressions[0]) as ConstantExpression).Value);
            }
            else
            {
                length = node.Expressions.Count;
            }

            var array = Array.CreateInstance(node.Type.GetElementType(), length);

            if (node.NodeType == ExpressionType.NewArrayInit)
            {
                for (int i = 0; i < length; i++)
                {
                    array.SetValue((sqlExpressionVisitor.Visit(node.Expressions[i]) as ConstantExpression).Value, i);
                }
            }

            if (_resolveType.HasFlag(ResolveType.SqlString))
            {
                return VisitConstant(Expression.Constant(string.Join(",", array as object[])));
            }

            return VisitConstant(Expression.Constant(array));
        }

        /// <summary>
        /// 访问方法
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (node.Type.Equals(typeof(bool)))
            {
                _equal = true;
            }
            if (_resolveType.HasFlag(ResolveType.SqlString) && ExpressionMapper._methodCall.ContainsKey(node.Method.Name))
            {
                return ExpressionMapper._methodCall[node.Method.Name].Invoke(_resolveType, _options.DbType, _sqlInfo, node, e => Visit(e));
            }

            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);

            object obj = null;

            if (!node.Method.IsStatic)
            {
                var ex = sqlExpressionVisitor.Visit(node.Object);
                if (ex.NodeType == ExpressionType.Parameter)
                {
                    // Push 已解析字符串 无需重复解析
                    _sqlInfo.SqlStack.Push(string.Join("", sqlExpressionVisitor._sqlInfo.SqlStack.ToList()));
                    // 追加参数
                    _sqlInfo.SqlParameters.Append(sqlExpressionVisitor._sqlInfo.SqlParameters);
                    return node;
                }
                obj = (ex as ConstantExpression).Value;
            }

            var args = new List<object>();
            foreach (var item in node.Arguments)
            {
                if (item.Type.FullName.StartsWith("System.Linq.Expressions"))
                {
                    if (item is UnaryExpression)
                    {
                        args.Add((item as UnaryExpression).Operand);
                    }
                }
                else
                {
                    args.Add((sqlExpressionVisitor.Visit(item) as ConstantExpression).Value);
                }
            }
            if (args.Count > 0 && args[0].GetType()?.GetInterface("Fast.Framework.Interfaces.IQuery") != null && !_subcall)
            {
                _sqlInfo.SqlStack.Push($"({args[0]})");
                return Expression.Constant(args[0]);
            }
            var value = node.Method.Invoke(obj, args.ToArray());
            if (_equal)
            {
                value = _resolveType.HasFlag(ResolveType.Not) ? !Convert.ToBoolean(value) : value;
            }
            _equal = false;
            return Visit(Expression.Constant(value));
        }

        /// <summary>
        /// 访问成员初始化
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            var expressions = (node.Reduce() as BlockExpression).Expressions.Skip(1).SkipLast(1).ToList();//去除不需要的表达式;

            if (_resolveType.HasFlag(ResolveType.SqlString))
            {
                for (int i = 0; i < expressions.Count; i++)
                {
                    var binaryExpression = (BinaryExpression)expressions[i];
                    Visit(binaryExpression.Right);
                    var name = AddIdentifier(node.Bindings[i].Member.IsDefined(typeof(ColumnAttribute)) ? node.Bindings[i].Member.GetCustomAttribute<ColumnAttribute>().Name : node.Bindings[i].Member.Name);
                    var value = string.Join("", _sqlInfo.SqlStack.ToList());
                    _sqlInfo.NewKeyValues.Add(name, value);
                    _sqlInfo.NewNames.Add(name);
                    _sqlInfo.NewValues.Add(value);
                    _sqlInfo.NewAssignMapper.Add($"{name} = {(value == "" ? "NULL" : value)}");
                    _sqlInfo.NewAsMapper.Add($"{(value == "" ? "NULL" : value)} AS {name}");
                    _sqlInfo.SqlStack.Clear();
                }
                return node;
            }
            var sqlExpressionVisitor = new SqlExpressionVisitor(_options, ResolveType.Call, true);
            sqlExpressionVisitor._parameterIndex.Append(_parameterIndex);
            var obj = (sqlExpressionVisitor.Visit(node.NewExpression) as ConstantExpression).Value;
            for (int i = 0; i < expressions.Count; i++)
            {
                if (node.Bindings[i].Member.MemberType == MemberTypes.Field)
                {
                    var fields = (FieldInfo)node.Bindings[i].Member;
                    fields.SetValue(obj, (sqlExpressionVisitor.Visit((expressions[i] as BinaryExpression).Right) as ConstantExpression).Value);
                }
                if (node.Bindings[i].Member.MemberType == MemberTypes.Property)
                {
                    var propertyInfo = (PropertyInfo)node.Bindings[i].Member;
                    propertyInfo.SetValue(obj, (sqlExpressionVisitor.Visit((expressions[i] as BinaryExpression).Right) as ConstantExpression).Value);
                }
            }
            return VisitConstant(Expression.Constant(obj));
        }

        /// <summary>
        /// 访问成员
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression?.NodeType == ExpressionType.Parameter)
            {
                if (_memberInfos.Count > 0)
                {
                    throw new Exception($"Parameter 不支持多个属性解析,请将{string.Join(" ", _memberInfos.ToList().Select(s => s.Name))}去除,或调用方法");
                }
                if (_resolveType.HasFlag(ResolveType.SqlString) && !_equal)
                {
                    if (node.Member.MemberType == MemberTypes.Field)
                    {
                        var fieldInfo = (FieldInfo)node.Member;
                        if (fieldInfo.FieldType.Equals(typeof(bool)))
                        {
                            if (_options.DbType == DbType.PostgreSQL)
                            {
                                _sqlInfo.SqlStack.Push($" = {(_resolveType.HasFlag(ResolveType.Not) ? "False" : "True")}");
                            }
                            else
                            {
                                _sqlInfo.SqlStack.Push($" = {(_resolveType.HasFlag(ResolveType.Not) ? 0 : 1)}");
                            }
                        }
                    }
                    if (node.Member.MemberType == MemberTypes.Property)
                    {
                        var propertyInfo = (PropertyInfo)node.Member;
                        if (propertyInfo.PropertyType.Equals(typeof(bool)))
                        {
                            if (_options.DbType == DbType.PostgreSQL)
                            {
                                _sqlInfo.SqlStack.Push($" = {(_resolveType.HasFlag(ResolveType.Not) ? "False" : "True")}");
                            }
                            else
                            {
                                _sqlInfo.SqlStack.Push($" = {(_resolveType.HasFlag(ResolveType.Not) ? 0 : 1)}");
                            }
                        }
                    }
                    _resolveType = ResolveType.SqlString;// 初始化
                }
                if (node.Member.IsDefined(typeof(ColumnAttribute)))
                {
                    _sqlInfo.SqlStack.Push(AddIdentifier(node.Member.GetCustomAttribute<ColumnAttribute>().Name));
                }
                else
                {
                    _sqlInfo.SqlStack.Push(AddIdentifier(node.Member.Name));
                }
            }
            else
            {
                _memberInfos.Push(node.Member);
                if (node.Expression == null && node.NodeType == ExpressionType.MemberAccess)
                {
                    return Visit(Expression.Constant(null));//子表达式为空但有成员处理
                }
            }
            return Visit(node.Expression);
        }

        /// <summary>
        /// 访问常量
        /// </summary>
        /// <param name="node">节点</param>
        /// <returns></returns>
        protected override Expression VisitConstant(ConstantExpression node)
        {
            var value = node.Value;
            foreach (var member in _memberInfos)
            {
                if (member.MemberType == MemberTypes.Field)
                {
                    var fieldInfo = (FieldInfo)member;
                    value = fieldInfo.GetValue(value);
                }
                else if (member.MemberType == MemberTypes.Property)
                {
                    var propertyInfo = (PropertyInfo)member;
                    value = propertyInfo.GetValue(value);
                }
            }
            if (_resolveType.HasFlag(ResolveType.SqlString) && value != null && value.GetType().Equals(typeof(bool)))
            {
                var b = Convert.ToBoolean(value);
                if (_resolveType.HasFlag(ResolveType.Not))
                {
                    value = !b;//取反
                    _resolveType = ResolveType.SqlString;// 初始化
                }
                if (_memberInfos.Count == 0 && !_equal)
                {
                    if (Convert.ToBoolean(value))
                    {
                        _sqlInfo.SqlStack.Push($"{(_resolveType.HasFlag(ResolveType.Not) ? "1 = 0" : "1 = 1")}");
                    }
                    else
                    {
                        _sqlInfo.SqlStack.Push($"{(_resolveType.HasFlag(ResolveType.Not) ? "1 = 1" : "1 = 0")}");
                    }
                    return node;
                }
                else if (!_options.DbType.Equals(DbType.PostgreSQL))
                {
                    value = b ? 1 : 0;
                }
            }
            if (!_resolveType.HasFlag(ResolveType.Call))
            {
                if (value != null)// 不等于null
                {
                    if (value.GetType().Equals(typeof(DateTime)))
                    {
                        value = Convert.ToDateTime(value).ToString(_options.DateTimeFormat);//格式化日期类型
                    }
                    if (_memberInfos.Count > 0 && !value.GetType().IsValueType)// 值类型不生成参数 提高性能
                    {
                        var parameterName = Guid.NewGuid().ToString().Replace("-", "");
                        _sqlInfo.SqlStack.Push($"{DbSymbolMapper.parameter[_options.DbType]}{parameterName}");
                        _sqlInfo.SqlParameters.Add(parameterName, AddWildcard(value));
                    }
                    else
                    {
                        value = AddQuotes(AddWildcard(value));
                        _sqlInfo.SqlStack.Push(Convert.ToString(value));
                    }
                }
            }

            //最后一个节点初始化
            _memberInfos.Clear();
            return Expression.Constant(value);//返回新的常量表达式
        }

        #region 私有公共方法

        /// <summary>
        /// 添加引号
        /// </summary>
        /// <param name="value">值</param>
        /// <returns></returns>
        private static object AddQuotes(object value)
        {
            if (value == null)
            {
                return null;
            }
            var type = value.GetType();
            if (type.Equals(typeof(char)) || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)))
            {
                return $"'{value}'";
            }
            return value;
        }

        /// <summary>
        /// 添加识别符
        /// </summary>
        /// <param name="name">名称</param>
        /// <returns></returns>
        private string AddIdentifier(string name)
        {
            return DbSymbolMapper.identify[_options.DbType].Insert(1, name);
        }

        /// <summary>
        /// 添加通配符
        /// </summary>
        /// <param name="value">值</param>
        /// <returns></returns>
        private object AddWildcard(object value)
        {
            if (_resolveType == ResolveType.LeftLike)
            {
                return $"%{value}";
            }
            else if (_resolveType == ResolveType.RightLike)
            {
                return $"{value}%";
            }
            else if (_resolveType == ResolveType.Like)
            {
                return $"%{value}%";
            }
            return value;
        }
        #endregion

    }
}
